home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / pc / ELYVER10.ZIP / MIKXMAS.ZIP / source / drv_ss.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-05  |  17.6 KB  |  715 lines

  1. /*
  2.  
  3. Name:
  4. DRV_SS.C
  5.  
  6. Description:
  7. Mikmod driver for output on Ensoniq Soundscape / Soundscape ELITE
  8.  
  9. Portability:
  10.  
  11. MSDOS:    BC(y)    Watcom(y)    DJGPP(y)
  12. Win95:    n
  13. Os2:    n
  14. Linux:    n
  15.  
  16. (y) - yes
  17. (n) - no (not possible or not useful)
  18. (?) - may be possible, but not tested
  19.  
  20. */
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <dos.h>
  24. #include <malloc.h>
  25. #include <conio.h>
  26. #include <string.h>
  27.  
  28. #include "mikmod.h"
  29. #include "mdma.h"
  30. #include "mirq.h"
  31.  
  32. /***************************************************************************
  33. >>>>>>>>>>>>>>>>>>>>>>>>> Lowlevel SS stuff <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  34. ***************************************************************************/
  35.  
  36. /* Ensoniq gate-array chip defines ... */
  37.  
  38. #define ODIE            0       /* ODIE gate array */
  39. #define OPUS            1       /* OPUS gate array */
  40. #define MMIC            2       /* MiMIC gate array */
  41.  
  42. /* relevant direct register defines - offsets from base address */
  43. #define GA_HOSTCTL_OFF  2       /* host port ctrl/stat reg */
  44. #define GA_ADDR_OFF     4       /* indirect address reg */
  45. #define GA_DATA_OFF     5       /* indirect data reg */
  46. #define GA_CODEC_OFF    8       /* for some boards CoDec is fixed from base */
  47.  
  48. /* relevant indirect register defines */
  49. #define GA_DMAB_REG     3       /* DMA chan B assign reg */
  50. #define GA_INTCFG_REG   4       /* interrupt configuration reg */
  51. #define GA_DMACFG_REG   5       /* DMA configuration reg */
  52. #define GA_CDCFG_REG    6       /* CD-ROM/CoDec config reg */
  53. #define GA_HMCTL_REG    9    /* host master control reg */
  54.  
  55.  
  56. /* AD-1848 or compatible CoDec defines ... */
  57. /* relevant direct register defines - offsets from base */
  58. #define CD_ADDR_OFF     0       /* indirect address reg */
  59. #define CD_DATA_OFF     1       /* indirect data reg */
  60. #define CD_STATUS_OFF   2       /* status register */
  61.  
  62. /* relevant indirect register defines */
  63. #define CD_ADCL_REG        0    /* left DAC input control reg */
  64. #define CD_ADCR_REG        1    /* right DAC input control reg */
  65. #define CD_CDAUXL_REG   2       /* left DAC output control reg */
  66. #define CD_CDAUXR_REG   3       /* right DAC output control reg */
  67. #define CD_DACL_REG     6       /* left DAC output control reg */
  68. #define CD_DACR_REG     7       /* right DAC output control reg */
  69. #define CD_FORMAT_REG   8       /* clock and data format reg */
  70. #define CD_CONFIG_REG   9       /* interface config register */
  71. #define CD_PINCTL_REG   10      /* external pin control reg */
  72. #define CD_UCOUNT_REG   14      /* upper count reg */
  73. #define CD_LCOUNT_REG   15      /* lower count reg */
  74. #define CD_XFORMAT_REG    28    /* extended format reg - 1845 record */
  75. #define CD_XUCOUNT_REG    30    /* extended upper count reg - 1845 record */
  76. #define CD_XLCOUNT_REG    31    /* extended lower count reg - 1845 record */
  77.  
  78. #define CD_MODE_CHANGE    0x40    /* mode change mask for addr reg */
  79.  
  80. /****************************************************************************
  81. hardware config info ...
  82. ****************************************************************************/
  83.  
  84. static UWORD    BasePort;    /* Gate Array/MPU-401 base port */
  85. static UWORD    MidiIrq;    /* the MPU-401 IRQ */
  86. static UWORD    WavePort;    /* the AD-1848 base port */
  87. static UWORD    WaveIrq;    /* the PCM IRQ */
  88. static UWORD    DmaChan;    /* the PCM DMA channel */
  89.  
  90. /****************************************************************************
  91. all kinds of stuff ...
  92. ****************************************************************************/
  93.  
  94. static UWORD Windx;        /* Wave IRQ index - for reg writes */
  95. static UWORD Mindx;        /* MIDI IRQ index - for reg writes */
  96.  
  97. static UBYTE IcType;        /* the Ensoniq chip type */
  98. static UBYTE CdCfgSav;        /* gate array register save area */
  99. static UBYTE DmaCfgSav;        /* gate array register save area */
  100. static UBYTE IntCfgSav;        /* gate array register save area */
  101.  
  102. static UWORD const SsIrqs[4] = { 9, 5, 7, 10 };  /* Soundscape IRQs */
  103. static UWORD const RsIrqs[4] = { 9, 7, 5, 15 };  /* an older IRQ set */
  104. static UWORD const *Irqs;           /* pointer to one of the IRQ sets */
  105.  
  106. static UBYTE DacSavL;        /* DAC left volume save */
  107. static UBYTE DacSavR;        /* DAC right volume save */
  108. static UBYTE CdxSavL;        /* CD/Aux left volume save */
  109. static UBYTE CdxSavR;        /* CD/Aux right volume save */
  110. static UBYTE AdcSavL;        /* ADC left volume save */
  111. static UBYTE AdcSavR;        /* ADC right volume save */
  112.  
  113. static DMAMEM *SS_DMAMEM;
  114. static char   *SS_DMABUF;
  115.  
  116.  
  117. /***************************************************************************
  118. >>>>>>>>>>>>>>>>>>>>>>>>> The actual SS driver <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  119. ***************************************************************************/
  120.  
  121.  
  122.  
  123. UBYTE GaRead(UWORD rnum)
  124. /*
  125.   This function is used to read the indirect addressed registers in the
  126.   Ensoniq Soundscape gate array.
  127.  
  128.   INPUTS:
  129.     rnum  - the numner of the indirect register to be read
  130.  
  131.   RETURNS:
  132.     the contents of the indirect register are returned
  133. */
  134. {
  135.     outportb(BasePort + GA_ADDR_OFF, rnum);
  136.     return inportb(BasePort + GA_DATA_OFF);
  137. }
  138.  
  139.  
  140.  
  141. void GaWrite(UWORD rnum,UBYTE value)
  142. /*
  143.   This function is used to write the indirect addressed registers in the
  144.   Ensoniq Soundscape gate array.
  145.  
  146.   INPUTS:
  147.     rnum   - the numner of the indirect register to be read
  148.     value  - the byte value to be written to the indirect register
  149.  
  150.   RETURNS:
  151.     Nothing
  152. */
  153. {
  154.     outportb(BasePort + GA_ADDR_OFF, rnum);
  155.     outportb(BasePort + GA_DATA_OFF, value);
  156. }
  157.  
  158.  
  159. UBYTE CdRead(UWORD rnum)
  160. /*
  161.   This function is used to read the indirect addressed registers in the
  162.   AD-1848 or compatible CoDec. It will preserve the special function bits
  163.   in the upper-nibble of the indirect address register.
  164.  
  165.   INPUTS:
  166.     rnum  - the numner of the indirect register to be read
  167.  
  168.   RETURNS:
  169.     the contents of the indirect register are returned
  170.  
  171. */
  172. {
  173.     outportb(WavePort + CD_ADDR_OFF,
  174.         (inportb(WavePort + CD_ADDR_OFF) & 0xf0) | rnum);
  175.     return inportb(WavePort+CD_DATA_OFF);
  176. }
  177.  
  178.  
  179.  
  180. void CdWrite(UWORD rnum,UBYTE value)
  181. /*
  182.   This function is used to write the indirect addressed registers in the
  183.   Ad-1848 or compatible CoDec. It will preserve the special function bits
  184.   in the upper-nibble of the indirect address register.
  185.  
  186.   INPUTS:
  187.     rnum   - the numner of the indirect register to be read
  188.     value  - the byte value to be written to the indirect register
  189.  
  190.   RETURNS:
  191.     Nothing
  192. */
  193. {
  194.     outportb(WavePort + CD_ADDR_OFF,
  195.         (inportb(WavePort + CD_ADDR_OFF) & 0xf0) | rnum);
  196.     outportb(WavePort + CD_DATA_OFF, value);
  197. }
  198.  
  199.  
  200.  
  201.  
  202. void SetDacVol(UBYTE lvol,UBYTE rvol)
  203. /*
  204.   This function sets the left and right DAC output level in the CoDec.
  205.  
  206.   INPUTS:
  207.     lvol  - left volume, 0-127
  208.     rvol  - right volume, 0-127
  209.  
  210.   RETURNS:
  211.     Nothing
  212.  
  213. */
  214. {
  215.     CdWrite(CD_DACL_REG, ~(lvol >> 1) & 0x3f);
  216.     CdWrite(CD_DACR_REG, ~(rvol >> 1) & 0x3f);
  217. }
  218.  
  219.  
  220.  
  221. void SetCdRomVol(UBYTE lvol,UBYTE rvol)
  222. /*
  223.   This function sets the left and right CD-ROM output level in the CoDec.
  224.  
  225.   INPUTS:
  226.     lvol  - left volume, 0-127
  227.     rvol  - right volume, 0-127
  228.  
  229.   RETURNS:
  230.     Nothing
  231. */
  232. {
  233.     CdWrite(CD_CDAUXL_REG, ~(lvol >> 2) & 0x1f);
  234.     CdWrite(CD_CDAUXR_REG, ~(rvol >> 2) & 0x1f);
  235. }
  236.  
  237.  
  238. void SetAdcVol(UBYTE lvol,UBYTE rvol)
  239. /*
  240.   This function sets the left and right ADC input level in the CoDec.
  241.  
  242.   INPUTS:
  243.     lvol  - left volume, 0-127
  244.     rvol  - right volume, 0-127
  245.  
  246.   RETURNS:
  247.     Nothing
  248.  
  249. */
  250. {
  251.     CdWrite(CD_ADCL_REG, (CdRead(CD_ADCL_REG) & 0xf0) | (lvol & 0x7f) >> 3);
  252.     CdWrite(CD_ADCR_REG, (CdRead(CD_ADCR_REG) & 0xf0) | (rvol & 0x7f) >> 3);
  253. }
  254.  
  255.  
  256. void StopCoDec(void)
  257. {
  258.     UWORD i;
  259.  
  260.     CdWrite(CD_CONFIG_REG,CdRead(CD_CONFIG_REG)&0xfc);
  261.  
  262.     /* Let the CoDec receive its last DACK(s). The DMAC must not be */
  263.     /*  masked while the CoDec has DRQs pending. */
  264. /*    for(i=0; i<256; ++i )
  265.         if(!(inportb(DmacRegP->status) & (0x10 << DmaChan))) break;
  266. */
  267. }
  268.  
  269.  
  270.  
  271. BOOL GetConfigEntry(char *entry, char *dest, FILE *fp)
  272. /*
  273.   This function parses a file (SNDSCAPE.INI) for a left-hand string and,
  274.   if found, writes its associated right-hand value to a destination buffer.
  275.   This function is case-insensitive.
  276.  
  277.   INPUTS:
  278.     fp  - a file pointer to the open SNDSCAPE.INI config file
  279.     dst - the destination buffer pointer
  280.     lhp - a pointer to the right-hand string
  281.  
  282.   RETURNS:
  283.     1   - if successful
  284.     0   - if the right-hand string is not found or has no equate
  285. */
  286. {
  287.     char static str[83];
  288.     char static tokstr[33];
  289.     char *p;
  290.  
  291.     /* make a local copy of the entry, upper-case it */
  292.     strcpy(tokstr, entry);
  293.     strupr(tokstr);
  294.  
  295.     /* rewind the file and try to find it ... */
  296.     rewind(fp);
  297.  
  298.     for( ;; ) {
  299.         /* get the next string from the file */
  300.  
  301.         fgets(str, 83, fp);
  302.         if(feof(fp)) return 0;
  303.  
  304.         /* properly terminate the string */
  305.         for( p = str; *p != '\0'; ++p ) {
  306.             if( *p == ' ' || *p == '\t' || *p == 0x0a || *p == 0x0d ) {
  307.                 *p = '\0';
  308.                 break;
  309.             }
  310.         }
  311.  
  312.         /* see if it's an 'equate' string; if so, zero the '=' */
  313.         if( !(p = strchr(str, '=')) ) continue;
  314.         *p = '\0';
  315.  
  316.         /* upper-case the current string and test it */
  317.         strupr(str);
  318.         if( strcmp(str, tokstr) )
  319.             continue;
  320.  
  321.         /* it's our string - copy the right-hand value to buffer */
  322.         for( p = str + strlen(str) + 1; (*dest++ = *p++) != '\0'; );
  323.         break;
  324.     }
  325.     return 1;
  326. }
  327.  
  328.  
  329. static BOOL SS_IsThere(void)
  330. {
  331.     static char str[78];
  332.     char *envptr;
  333.     FILE *fp;
  334.     UBYTE tmp;
  335.  
  336.     if((envptr=getenv("SNDSCAPE"))==NULL) return 0;
  337.  
  338.     strcpy(str, envptr);
  339.     if( str[strlen(str) - 1] == '\\' )
  340.         str[strlen(str) - 1] = '\0';
  341.  
  342.     strcat(str, "\\SNDSCAPE.INI");
  343.  
  344.     if(!(fp=fopen(str, "r"))) return 0;
  345.  
  346.     /* read all of the necessary config info ... */
  347.     if(!GetConfigEntry("Product",str,fp)){
  348.         fclose(fp);
  349.         return 0;
  350.     }
  351.  
  352.     /* if an old product name is read, set the IRQs accordingly */
  353.     strupr(str);
  354.     if(strstr(str,"SOUNDFX") || strstr(str,"MEDIA_FX"))
  355.         Irqs = RsIrqs;
  356.     else
  357.         Irqs = SsIrqs;
  358.  
  359.     if(!GetConfigEntry("Port", str, fp)){
  360.         fclose(fp);
  361.         return 0;
  362.     }
  363.  
  364.     BasePort=strtol(str,NULL,16);
  365.  
  366.     if(!GetConfigEntry("WavePort",str,fp)){
  367.         fclose(fp);
  368.         return 0;
  369.     }
  370.  
  371.     WavePort=strtol(str,NULL,16);
  372.  
  373.     if(!GetConfigEntry("IRQ",str,fp)){
  374.         fclose(fp);
  375.         return 0;
  376.     }
  377.  
  378.     MidiIrq=strtol(str,NULL,10);
  379.     if(MidiIrq==2) MidiIrq = 9;
  380.  
  381.     if(!GetConfigEntry("SBIRQ",str,fp)){
  382.         fclose(fp);
  383.         return 0;
  384.     }
  385.  
  386.     WaveIrq=strtol(str,NULL,10);
  387.     if(WaveIrq==2) WaveIrq=9;
  388.  
  389.     if(!GetConfigEntry("DMA",str,fp)){
  390.         fclose(fp);
  391.         return 0;
  392.     }
  393.  
  394.     DmaChan=strtol(str,NULL,10);
  395.  
  396.     fclose(fp);
  397.  
  398.     /* see if Soundscape is there by reading HW ... */
  399.     if((inportb(BasePort+GA_HOSTCTL_OFF)&0x78) != 0x00)    return 0;
  400.     if((inportb(BasePort+GA_ADDR_OFF)&0xf0)==0xf0) return 0;
  401.  
  402.     outportb(BasePort+GA_ADDR_OFF,0xf5);
  403.  
  404.     tmp=inportb(BasePort+GA_ADDR_OFF);
  405.  
  406.     if((tmp & 0xf0)==0xf0) return 0;
  407.     if((tmp & 0x0f)!=0x05) return 0;
  408.  
  409.     /* formulate the chip ID */
  410.     if( (tmp & 0x80) != 0x00 )
  411.         IcType = MMIC;
  412.     else if((tmp & 0x70) != 0x00)
  413.         IcType = OPUS;
  414.     else
  415.         IcType = ODIE;
  416.  
  417.     /* now do a quick check to make sure the CoDec is there too */
  418.     if((inportb(WavePort)&0x80)!=0x00) return 0;
  419.     return 1;
  420. }
  421.  
  422.  
  423. #ifdef NEVER
  424.  
  425. static void interrupt newhandler(void)
  426. {
  427.     if(WaveIrq==7){
  428.         outportb(0x20,0x0b);
  429.         inportb(0x21);
  430.         if(!(inportb(0x20)&0x80)) return;
  431.     }
  432.     else if(WaveIrq==15){
  433.         outportb(0xa0,0x0b);
  434.         inportb(0xa1);
  435.         if(!(inportb(0xa0)&0x80)) return;
  436.     }
  437.  
  438.     interruptcount++;
  439.  
  440.     /* if the CoDec is interrupting clear the AD-1848 interrupt */
  441.     if(inportb(WavePort+CD_STATUS_OFF)&0x01)
  442.         outportb(WavePort+CD_STATUS_OFF,0x00);
  443.  
  444.     MIrq_EOI(WaveIrq);
  445. }
  446.  
  447.  
  448. static PVI oldhandler;
  449.  
  450. #endif
  451.  
  452.  
  453. static UWORD codecfreqs[14]={
  454.     5512, 6615, 8000, 9600,11025,16000,18900,
  455.     22050,27428,32000,33075,37800,44100,48000
  456. };
  457.  
  458.  
  459. static UWORD codecformats[14]={
  460.     0x01, 0x0f, 0x00, 0x0e,    0x03, 0x02, 0x05,
  461.     0x07, 0x04,    0x06, 0x0d,    0x09, 0x0b,    0x0c
  462. };
  463.  
  464.  
  465. static UBYTE codecformat;
  466.  
  467.  
  468. static BOOL SS_Init(void)
  469. {
  470.     int t;
  471.  
  472.     if(!SS_IsThere()){
  473.         myerr="No such hardware detected, check your 'SNDSCAPE' env. variable";
  474.         return 0;
  475.     }
  476.  
  477.     printf("Ensoniq Soundscape at port 0x%x, irq %d, dma %d\n",WavePort,WaveIrq,DmaChan);
  478.  
  479.     /* find closest codec frequency */
  480.  
  481.     for(t=0;t<14;t++){
  482.         if(t==13 || md_mixfreq<=codecfreqs[t]){
  483.             md_mixfreq=codecfreqs[t];
  484.             break;
  485.         }
  486.     }
  487.  
  488.     codecformat=codecformats[t];
  489.     if(md_mode & DMODE_STEREO) codecformat|=0x10;
  490.     if(md_mode & DMODE_16BITS) codecformat|=0x40;
  491.  
  492.     if(!VC_Init()) return 0;
  493.  
  494.     SS_DMAMEM=MDma_AllocMem(md_dmabufsize);
  495.  
  496.     if(SS_DMAMEM==NULL){
  497.         VC_Exit();
  498.         myerr="Couldn't allocate page-contiguous dma-buffer";
  499.         return 0;
  500.     }
  501.  
  502.     SS_DMABUF=(char *)MDma_GetPtr(SS_DMAMEM);
  503.  
  504.     /* In case the CoDec is running, stop it */
  505.     StopCoDec();
  506.  
  507.     /* Clear possible CoDec and SoundBlaster emulation interrupts */
  508.     outportb(WavePort+CD_STATUS_OFF,0x00);
  509.     inportb(0x22e);
  510.  
  511.     /* If necessary, save some regs, do some resource re-routing */
  512.     if( IcType != MMIC) {
  513.  
  514.         /* derive the MIDI and Wave IRQ indices (0-3) for reg writes */
  515.         for( Mindx = 0; Mindx < 4; ++Mindx )
  516.             if( MidiIrq == *(Irqs + Mindx) )
  517.                 break;
  518.         for( Windx = 0; Windx < 4; ++Windx )
  519.             if( WaveIrq == *(Irqs + Windx) )
  520.                 break;
  521.  
  522.         /* setup the CoDec DMA polarity */
  523.         GaWrite(GA_DMACFG_REG, 0x50);
  524.  
  525.         /* give the CoDec control of the DMA and Wave IRQ resources */
  526.             CdCfgSav = GaRead(GA_CDCFG_REG);
  527.         GaWrite(GA_CDCFG_REG, 0x89 | (DmaChan << 4) | (Windx << 1));
  528.  
  529.         /* pull the Sound Blaster emulation off of those resources */
  530.         DmaCfgSav = GaRead(GA_DMAB_REG);
  531.         GaWrite(GA_DMAB_REG, 0x20);
  532.         IntCfgSav = GaRead(GA_INTCFG_REG);
  533.         GaWrite(GA_INTCFG_REG, 0xf0 | (Mindx << 2) | Mindx);
  534.     }
  535.  
  536.     /* Save all volumes that we might use, init some levels */
  537.     DacSavL = CdRead(CD_DACL_REG);
  538.     DacSavR = CdRead(CD_DACR_REG);
  539.     CdxSavL = CdRead(CD_CDAUXL_REG);
  540.     CdxSavR = CdRead(CD_CDAUXL_REG);
  541.     AdcSavL = CdRead(CD_ADCL_REG);
  542.     AdcSavR = CdRead(CD_ADCR_REG);
  543.  
  544.     SetDacVol(127, 127);
  545.     SetAdcVol(96, 96);
  546.  
  547.     /* Select the mic/line input to the record mux; */
  548.     /* if not ODIE, set the mic gain bit too */
  549.     CdWrite(CD_ADCL_REG, (CdRead(CD_ADCL_REG) & 0x3f) |
  550.         (IcType == ODIE ? 0x80 : 0xa0));
  551.     CdWrite(CD_ADCR_REG, (CdRead(CD_ADCR_REG) & 0x3f) |
  552.         (IcType == ODIE ? 0x80 : 0xa0));
  553.  
  554.     /* Put the CoDec into mode change state */
  555.     outportb(WavePort + CD_ADDR_OFF, 0x40);
  556.  
  557.     /* Setup CoDec mode - single DMA chan, AutoCal on */
  558.     CdWrite(CD_CONFIG_REG, 0x0c);
  559.  
  560. #ifdef NEVER
  561.     /* enable the CoDec interrupt pin */
  562.     CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) | 0x02);
  563.     oldhandler=MIrq_SetHandler(WaveIrq,newhandler);
  564.     MIrq_OnOff(WaveIrq,1);
  565. #else
  566.     /* disable the interrupt for mikmod */
  567.     CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) & 0xfd);
  568. #endif
  569.     return 1;
  570. }
  571.  
  572.  
  573.  
  574. static void SS_Exit(void)
  575. {
  576.     /* in case the CoDec is running, stop it */
  577.     StopCoDec();
  578.  
  579.     /* mask the PC DMA Controller */
  580. /*    outportb(DmacRegP->mask, 0x04 | DmaChan); */
  581.  
  582. #ifdef NEVER
  583.     /* disable the CoDec interrupt pin */
  584.     CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) & 0xfd);
  585.     MIrq_OnOff(WaveIrq,0);
  586.     MIrq_SetHandler(WaveIrq,oldhandler);
  587. #endif
  588.  
  589.     /* restore all volumes ... */
  590.     CdWrite(CD_DACL_REG, DacSavL);
  591.     CdWrite(CD_DACR_REG, DacSavR);
  592.     CdWrite(CD_CDAUXL_REG, CdxSavL);
  593.     CdWrite(CD_CDAUXL_REG, CdxSavR);
  594.     CdWrite(CD_ADCL_REG, AdcSavL);
  595.     CdWrite(CD_ADCR_REG, AdcSavR);
  596.  
  597.     /* if necessary, restore gate array resource registers */
  598.     if(IcType!=MMIC){
  599.         GaWrite(GA_INTCFG_REG, IntCfgSav);
  600.         GaWrite(GA_DMAB_REG, DmaCfgSav);
  601.         GaWrite(GA_CDCFG_REG, CdCfgSav);
  602.     }
  603.  
  604.     MDma_FreeMem(SS_DMAMEM);
  605.     VC_Exit();
  606. }
  607.  
  608.  
  609. static UWORD last=0;
  610. static UWORD curr=0;
  611.  
  612.  
  613. static void SS_Update(void)
  614. {
  615.     UWORD todo,index;
  616.  
  617.     curr=(md_dmabufsize-MDma_Todo(DmaChan))&0xfffc;
  618.  
  619.     if(curr>=md_dmabufsize) return;
  620.     if(curr==last) return;
  621.  
  622.     if(curr>last){
  623.         todo=curr-last; index=last;
  624.         last+=VC_WriteBytes(&SS_DMABUF[index],todo);
  625.         MDma_Commit(SS_DMAMEM,index,todo);
  626.         if(last>=md_dmabufsize) last=0;
  627.     }
  628.     else{
  629.         todo=md_dmabufsize-last;
  630.         VC_WriteBytes(&SS_DMABUF[last],todo);
  631.         MDma_Commit(SS_DMAMEM,last,todo);
  632.         last=VC_WriteBytes(SS_DMABUF,curr);
  633.         MDma_Commit(SS_DMAMEM,0,curr);
  634.     }
  635. }
  636.  
  637.  
  638. static void SS_PlayStart(void)
  639. {
  640.     int direction=0;
  641.     long i;
  642.     UWORD tmp;
  643.  
  644.     VC_PlayStart();
  645.  
  646.     /* make sure the the CoDec is in mode change state */
  647.     outportb(WavePort + CD_ADDR_OFF, 0x40);
  648.  
  649.     /* and write the format register */
  650.     CdWrite(CD_FORMAT_REG, codecformat);
  651.  
  652.     /* if not using ODIE and recording, setup extended format register */
  653.     if( IcType != ODIE && direction )
  654.         CdWrite(CD_XFORMAT_REG, codecformat & 0x70);
  655.  
  656.     /* delay for internal re-synch */
  657.     for( i = 0; i < 200000UL; ++i )
  658.         inportb(BasePort + GA_ADDR_OFF);
  659.  
  660.     /* clear the dma buffer */
  661.  
  662.     VC_SilenceBytes(SS_DMABUF,md_dmabufsize);
  663.     MDma_Commit(SS_DMAMEM,0,md_dmabufsize);
  664.  
  665.     /* Write the CoDec interrupt count - sample frames per half-buffer. */
  666.     /* If not using ODIE and recording, use extended count regs */
  667.     tmp = md_dmabufsize;
  668.     if(md_mode&DMODE_STEREO) tmp>>=1;
  669.     if(md_mode&DMODE_16BITS) tmp>>=1;
  670.     tmp--;
  671.  
  672.     if( IcType != ODIE && direction ) {
  673.         CdWrite(CD_XLCOUNT_REG, tmp);
  674.         CdWrite(CD_XUCOUNT_REG, tmp >> 8);
  675.     }
  676.     else {
  677.         CdWrite(CD_LCOUNT_REG, tmp);
  678.         CdWrite(CD_UCOUNT_REG, tmp >> 8);
  679.     }
  680.  
  681.     if(!MDma_Start(DmaChan,SS_DMAMEM,md_dmabufsize,INDEF_WRITE)){
  682.         return;
  683.     }
  684.  
  685.     /* disable mode change state and start the CoDec */
  686.     outportb(WavePort + CD_ADDR_OFF, 0x00);
  687.     CdWrite(CD_CONFIG_REG, direction ? 0x02 : 0x01);
  688. }
  689.  
  690.  
  691. static void SS_PlayStop(void)
  692. {
  693.     StopCoDec();
  694.     VC_PlayStop();
  695. }
  696.  
  697.  
  698. DRIVER drv_ss={
  699.     NULL,
  700.     "Ensoniq Soundscape",
  701.     "MikMod Ensoniq Soundscape Driver v0.0 - Thanks to CyberCerus",
  702.     SS_IsThere,
  703.     VC_SampleLoad,
  704.     VC_SampleUnload,
  705.     SS_Init,
  706.     SS_Exit,
  707.     SS_PlayStart,
  708.     SS_PlayStop,
  709.     SS_Update,
  710.     VC_VoiceSetVolume,
  711.     VC_VoiceSetFrequency,
  712.     VC_VoiceSetPanning,
  713.     VC_VoicePlay
  714. };
  715.